home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / imake / RCS / imake.c,v < prev    next >
Encoding:
Text File  |  1991-10-22  |  19.2 KB  |  852 lines

  1. head     1.1;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    rab:1.1; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.1
  10. date     91.10.22.12.49.32;  author rab;  state Exp;
  11. branches ;
  12. next     ;
  13.  
  14.  
  15. desc
  16. @@
  17.  
  18.  
  19.  
  20. 1.1
  21. log
  22. @Initial revision
  23. @
  24. text
  25. @/* $XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $ */
  26.  
  27. /*****************************************************************************\
  28.  *                                                                           *
  29.  *                                Porting Note                               *
  30.  *                                                                           *
  31.  * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will be *
  32.  * passed to the template file.                                              *
  33.  *                                                                           *
  34. \*****************************************************************************/
  35.  
  36. /*
  37.  * 
  38.  * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
  39.  * 
  40.  * Permission to use, copy, modify, and distribute this
  41.  * software and its documentation for any purpose and without
  42.  * fee is hereby granted, provided that the above copyright
  43.  * notice appear in all copies and that both that copyright
  44.  * notice and this permission notice appear in supporting
  45.  * documentation, and that the name of M.I.T. not be used in
  46.  * advertising or publicity pertaining to distribution of the
  47.  * software without specific, written prior permission.
  48.  * M.I.T. makes no representations about the suitability of
  49.  * this software for any purpose.  It is provided "as is"
  50.  * without express or implied warranty.
  51.  * 
  52.  * Original Author:
  53.  *    Todd Brunhoff
  54.  *    Tektronix, inc.
  55.  *    While a guest engineer at Project Athena, MIT
  56.  *
  57.  * imake: the include-make program.
  58.  *
  59.  * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-e] [-v] [make flags]
  60.  *
  61.  * Imake takes a template makefile (Imake.tmpl) and runs cpp on it
  62.  * producing a temporary makefile in /tmp.  It then runs make on
  63.  * this pre-processed makefile.
  64.  * Options:
  65.  *        -D    define.  Same as cpp -D argument.
  66.  *        -I    Include directory.  Same as cpp -I argument.
  67.  *        -T    template.  Designate a template other
  68.  *             than Imake.tmpl
  69.  *        -s[F]    show.  Show the produced makefile on the standard
  70.  *            output.  Make is not run is this case.  If a file
  71.  *            argument is provided, the output is placed there.
  72.  *              -e[F]   execute instead of show; optionally name Makefile F
  73.  *        -v    verbose.  Show the make command line executed.
  74.  *
  75.  * Environment variables:
  76.  *        
  77.  *        IMAKEINCLUDE    Include directory to use in addition to "."
  78.  *        IMAKECPP    Cpp to use instead of /lib/cpp
  79.  *        IMAKEMAKE    make program to use other than what is
  80.  *                found by searching the $PATH variable.
  81.  * Other features:
  82.  *    imake reads the entire cpp output into memory and then scans it
  83.  *    for occurences of "@@@@".  If it encounters them, it replaces it with
  84.  *    a newline.  It also trims any trailing white space on output lines
  85.  *    (because make gets upset at them).  This helps when cpp expands
  86.  *    multi-line macros but you want them to appear on multiple lines.
  87.  *
  88.  *    The macros MAKEFILE and MAKE are provided as macros
  89.  *    to make.  MAKEFILE is set to imake's makefile (not the constructed,
  90.  *    preprocessed one) and MAKE is set to argv[0], i.e. the name of
  91.  *    the imake program.
  92.  *
  93.  * Theory of operation:
  94.  *   1. Determine the name of the imakefile from the command line (-f)
  95.  *    or from the content of the current directory (Imakefile or imakefile).
  96.  *    Call this <imakefile>.  This gets added to the arguments for
  97.  *    make as MAKEFILE=<imakefile>.
  98.  *   2. Determine the name of the template from the command line (-T)
  99.  *    or the default, Imake.tmpl.  Call this <template>
  100.  *   3. Start up cpp an provide it with three lines of input:
  101.  *        #define IMAKE_TEMPLATE        " <template> "
  102.  *        #define INCLUDE_IMAKEFILE    < <imakefile> >
  103.  *        #include IMAKE_TEMPLATE
  104.  *    Note that the define for INCLUDE_IMAKEFILE is intended for
  105.  *    use in the template file.  This implies that the imake is
  106.  *    useless unless the template file contains at least the line
  107.  *        #include INCLUDE_IMAKEFILE
  108.  *   4. Gather the output from cpp, and clean it up, expanding @@@@ to
  109.  *    newlines, stripping trailing white space, cpp control lines,
  110.  *    and extra blank lines.  This cleaned output is placed in a
  111.  *    temporary file.  Call this <makefile>.
  112.  *   5. Start up make specifying <makefile> as its input.
  113.  *
  114.  * The design of the template makefile should therefore be:
  115.  *    <set global macros like CFLAGS, etc.>
  116.  *    <include machine dependent additions>
  117.  *    #include INCLUDE_IMAKEFILE
  118.  *    <add any global targets like 'clean' and long dependencies>
  119.  */
  120. #include <stdio.h>
  121. #if (defined(SVR4) || defined(_IBMR2) || defined(SYSV386)) && __STDC__
  122. FILE * fdopen();
  123. #endif
  124. #include <ctype.h>
  125. #include "Xosdefs.h"
  126. #ifndef X_NOT_POSIX
  127. #define _POSIX_SOURCE
  128. #endif
  129. #include <sys/types.h>
  130. #include <fcntl.h>
  131. #ifdef X_NOT_POSIX
  132. #include <sys/file.h>
  133. #else
  134. #include <unistd.h>
  135. #endif
  136. #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
  137. #include <signal.h>
  138. #else
  139. #define _POSIX_SOURCE
  140. #include <signal.h>
  141. #undef _POSIX_SOURCE
  142. #endif
  143. #include <sys/stat.h>
  144. #ifndef X_NOT_POSIX
  145. #ifdef _POSIX_SOURCE
  146. #include <sys/wait.h>
  147. #else
  148. #define _POSIX_SOURCE
  149. #include <sys/wait.h>
  150. #undef _POSIX_SOURCE
  151. #endif
  152. #define waitCode(w)    WEXITSTATUS(w)
  153. #define waitSig(w)    WTERMSIG(w)
  154. typedef int        waitType;
  155. #else /* X_NOT_POSIX */
  156. #ifdef SYSV
  157. #define waitCode(w)    (((w) >> 8) & 0x7f)
  158. #define waitSig(w)    ((w) & 0xff)
  159. typedef int        waitType;
  160. #else /* SYSV */
  161. #include <sys/wait.h>
  162. #define waitCode(w)    ((w).w_T.w_Retcode)
  163. #define waitSig(w)    ((w).w_T.w_Termsig)
  164. typedef union wait    waitType;
  165. #endif
  166. #ifndef WIFSIGNALED
  167. #define WIFSIGNALED(w) waitSig(w)
  168. #endif
  169. #ifndef WIFEXITED
  170. #define WIFEXITED(w) waitCode(w)
  171. #endif
  172. #endif /* X_NOT_POSIX */
  173. #ifndef X_NOT_STDC_ENV
  174. #include <stdlib.h>
  175. #else
  176. char *malloc(), *realloc();
  177. void exit();
  178. #endif
  179. #if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
  180. char *malloc(), *realloc();
  181. #endif /* macII */
  182. #ifdef X_NOT_STDC_ENV
  183. extern char    *getenv();
  184. #endif
  185. #include <errno.h>
  186. extern int    errno;
  187. #include "imakemdep.h"
  188.  
  189.  
  190. #define    TRUE        1
  191. #define    FALSE        0
  192.  
  193. #ifdef FIXUP_CPP_WHITESPACE
  194. int    InRule = FALSE;
  195. #endif
  196.  
  197. /*
  198.  * Some versions of cpp reduce all tabs in macro expansion to a single
  199.  * space.  In addition, the escaped newline may be replaced with a
  200.  * space instead of being deleted.  Blech.
  201.  */
  202. #ifndef FIXUP_CPP_WHITESPACE
  203. #define KludgeOutputLine(arg)
  204. #define KludgeResetRule()
  205. #endif
  206.  
  207. typedef    unsigned char    boolean;
  208.  
  209. #ifndef DEFAULT_CPP
  210. #ifdef USE_CC_E
  211. #define DEFAULT_CPP "/bin/cc"
  212. #else
  213. #ifdef CPP_PROGRAM
  214. #define DEFAULT_CPP CPP_PROGRAM
  215. #else
  216. #define DEFAULT_CPP "/lib/cpp"
  217. #endif
  218. #endif
  219. #endif
  220.  
  221. char *cpp = DEFAULT_CPP;
  222.  
  223. char    *tmpMakefile    = "/tmp/Imf.XXXXXX";
  224. char    *tmpImakefile    = "/tmp/IIf.XXXXXX";
  225. char    *make_argv[ ARGUMENTS ] = { "make" };
  226.  
  227. int    make_argindex;
  228. int    cpp_argindex;
  229. char    *make = NULL;
  230. char    *Imakefile = NULL;
  231. char    *Makefile = "Makefile";
  232. char    *Template = "Imake.tmpl";
  233. char    *program;
  234. char    *FindImakefile();
  235. char    *ReadLine();
  236. char    *CleanCppInput();
  237. char    *Strdup();
  238. char    *Emalloc();
  239.  
  240. boolean    verbose = FALSE;
  241. boolean    show = TRUE;
  242.  
  243. main(argc, argv)
  244.     int    argc;
  245.     char    **argv;
  246. {
  247.     FILE    *tmpfd;
  248.     char    makeMacro[ BUFSIZ ];
  249.     char    makefileMacro[ BUFSIZ ];
  250.  
  251.     program = argv[0];
  252.     init();
  253.     SetOpts(argc, argv);
  254. #ifdef USE_CC_E
  255.     AddCppArg("-");
  256. #endif
  257.  
  258.     Imakefile = FindImakefile(Imakefile);
  259.     if (Makefile)
  260.         tmpMakefile = Makefile;
  261.     else {
  262.         tmpMakefile = Strdup(tmpMakefile);
  263.         (void) mktemp(tmpMakefile);
  264.     }
  265.     AddMakeArg("-f");
  266.     AddMakeArg( tmpMakefile );
  267.     sprintf(makeMacro, "MAKE=%s", program);
  268.     AddMakeArg( makeMacro );
  269.     sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
  270.     AddMakeArg( makefileMacro );
  271.  
  272.     if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
  273.         LogFatal("Cannot create temporary file %s.", tmpMakefile);
  274.  
  275.     cppit(Imakefile, Template, tmpfd, tmpMakefile);
  276.  
  277.     if (show) {
  278.         if (Makefile == NULL)
  279.             showit(tmpfd);
  280.     } else
  281.         makeit();
  282.     wrapup();
  283.     exit(0);
  284. }
  285.  
  286. showit(fd)
  287.     FILE    *fd;
  288. {
  289.     char    buf[ BUFSIZ ];
  290.     int    red;
  291.  
  292.     fseek(fd, 0, 0);
  293.     while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
  294.         fwrite(buf, red, 1, stdout);
  295.     if (red < 0)
  296.         LogFatal("Cannot write stdout.", "");
  297. }
  298.  
  299. wrapup()
  300. {
  301.     if (tmpMakefile != Makefile)
  302.         unlink(tmpMakefile);
  303.     unlink(tmpImakefile);
  304. }
  305.  
  306. #ifdef SIGNALRETURNSINT
  307. int
  308. #else
  309. void
  310. #endif
  311. catch(sig)
  312.     int    sig;
  313. {
  314.     errno = 0;
  315.     LogFatalI("Signal %d.", sig);
  316. }
  317.  
  318. /*
  319.  * Initialize some variables.
  320.  */
  321. init()
  322. {
  323.     char    *p;
  324.  
  325.     make_argindex=0;
  326.     while (make_argv[ make_argindex ] != NULL)
  327.         make_argindex++;
  328.     cpp_argindex = 0;
  329.     while (cpp_argv[ cpp_argindex ] != NULL)
  330.         cpp_argindex++;
  331.  
  332.     /*
  333.      * See if the standard include directory is different than
  334.      * the default.  Or if cpp is not the default.  Or if the make
  335.      * found by the PATH variable is not the default.
  336.      */
  337.     if (p = getenv("IMAKEINCLUDE")) {
  338.         if (*p != '-' || *(p+1) != 'I')
  339.             LogFatal("Environment var IMAKEINCLUDE %s\n",
  340.                 "must begin with -I");
  341.         AddCppArg(p);
  342.         for (; *p; p++)
  343.             if (*p == ' ') {
  344.                 *p++ = '\0';
  345.                 AddCppArg(p);
  346.             }
  347.     }
  348.     if (p = getenv("IMAKECPP"))
  349.         cpp = p;
  350.     if (p = getenv("IMAKEMAKE"))
  351.         make = p;
  352.  
  353.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  354.         signal(SIGINT, catch);
  355. }
  356.  
  357. AddMakeArg(arg)
  358.     char    *arg;
  359. {
  360.     errno = 0;
  361.     if (make_argindex >= ARGUMENTS-1)
  362.         LogFatal("Out of internal storage.", "");
  363.     make_argv[ make_argindex++ ] = arg;
  364.     make_argv[ make_argindex ] = NULL;
  365. }
  366.  
  367. AddCppArg(arg)
  368.     char    *arg;
  369. {
  370.     errno = 0;
  371.     if (cpp_argindex >= ARGUMENTS-1)
  372.         LogFatal("Out of internal storage.", "");
  373.     cpp_argv[ cpp_argindex++ ] = arg;
  374.     cpp_argv[ cpp_argindex ] = NULL;
  375. }
  376.  
  377. SetOpts(argc, argv)
  378.     int    argc;
  379.     char    **argv;
  380. {
  381.     errno = 0;
  382.     /*
  383.      * Now gather the arguments for make
  384.      */
  385.     for(argc--, argv++; argc; argc--, argv++) {
  386.         /*
  387.          * We intercept these flags.
  388.          */
  389.         if (argv[0][0] == '-') {
  390.         if (argv[0][1] == 'D') {
  391.             AddCppArg(argv[0]);
  392.         } else if (argv[0][1] == 'I') {
  393.             AddCppArg(argv[0]);
  394.         } else if (argv[0][1] == 'f') {
  395.             if (argv[0][2])
  396.             Imakefile = argv[0]+2;
  397.             else {
  398.             argc--, argv++;
  399.             if (! argc)
  400.                 LogFatal("No description arg after -f flag\n", "");
  401.             Imakefile = argv[0];
  402.             }
  403.         } else if (argv[0][1] == 's') {
  404.             if (argv[0][2])
  405.             Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
  406.                 NULL : argv[0]+2;
  407.             else {
  408.             argc--, argv++;
  409.             if (!argc)
  410.                 LogFatal("No description arg after -s flag\n", "");
  411.             Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
  412.                 NULL : argv[0];
  413.             }
  414.             show = TRUE;
  415.         } else if (argv[0][1] == 'e') {
  416.            Makefile = (argv[0][2] ? argv[0]+2 : NULL);
  417.            show = FALSE;
  418.         } else if (argv[0][1] == 'T') {
  419.             if (argv[0][2])
  420.             Template = argv[0]+2;
  421.             else {
  422.             argc--, argv++;
  423.             if (! argc)
  424.                 LogFatal("No description arg after -T flag\n", "");
  425.             Template = argv[0];
  426.             }
  427.         } else if (argv[0][1] == 'v') {
  428.             verbose = TRUE;
  429.         } else
  430.             AddMakeArg(argv[0]);
  431.         } else
  432.         AddMakeArg(argv[0]);
  433.     }
  434. }
  435.  
  436. char *FindImakefile(Imakefile)
  437.     char    *Imakefile;
  438. {
  439.     int    fd;
  440.  
  441.     if (Imakefile) {
  442.         if ((fd = open(Imakefile, O_RDONLY)) < 0)
  443.             LogFatal("Cannot open %s.", Imakefile);
  444.     } else {
  445.         if ((fd = open("Imakefile", O_RDONLY)) < 0)
  446.             if ((fd = open("imakefile", O_RDONLY)) < 0)
  447.                 LogFatal("No description file.", "");
  448.             else
  449.                 Imakefile = "imakefile";
  450.         else
  451.             Imakefile = "Imakefile";
  452.     }
  453.     close (fd);
  454.     return(Imakefile);
  455. }
  456.  
  457. LogFatalI(s, i)
  458.     char *s;
  459.     int i;
  460. {
  461.     /*NOSTRICT*/
  462.     LogFatal(s, (char *)i);
  463. }
  464.  
  465. LogFatal(x0,x1)
  466.     char *x0, *x1;
  467. {
  468.     extern char    *sys_errlist[];
  469.     static boolean    entered = FALSE;
  470.  
  471.     if (entered)
  472.         return;
  473.     entered = TRUE;
  474.  
  475.     fprintf(stderr, "%s: ", program);
  476.     if (errno)
  477.         fprintf(stderr, "%s: ", sys_errlist[ errno ]);
  478.     fprintf(stderr, x0,x1);
  479.     fprintf(stderr, "  Stop.\n");
  480.     wrapup();
  481.     exit(1);
  482. }
  483.  
  484. showargs(argv)
  485.     char    **argv;
  486. {
  487.     for (; *argv; argv++)
  488.         fprintf(stderr, "%s ", *argv);
  489.     fprintf(stderr, "\n");
  490. }
  491.  
  492. cppit(Imakefile, template, outfd, outfname)
  493.     char    *Imakefile;
  494.     char    *template;
  495.     FILE    *outfd;
  496.     char    *outfname;
  497. {
  498.     FILE    *pipeFile;
  499.     int    pid, pipefd[2];
  500.     waitType    status;
  501.     char    *cleanedImakefile;
  502.  
  503.     /*
  504.      * Get a pipe.
  505.      */
  506.     if (pipe(pipefd) < 0)
  507.         LogFatal("Cannot make a pipe.", "");
  508.  
  509.     /*
  510.      * Fork and exec cpp
  511.      */
  512.     pid = fork();
  513.     if (pid < 0)
  514.         LogFatal("Cannot fork.", "");
  515.     if (pid) {    /* parent */
  516.         close(pipefd[0]);
  517.         cleanedImakefile = CleanCppInput(Imakefile);
  518.         if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
  519.             LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
  520.         fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
  521.             template);
  522.         fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
  523.             cleanedImakefile);
  524.         fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
  525.         fclose(pipeFile);
  526.         while (wait(&status) > 0) {
  527.             errno = 0;
  528.             if (WIFSIGNALED(status))
  529.                 LogFatalI("Signal %d.", waitSig(status));
  530.             if (WIFEXITED(status) && waitCode(status))
  531.                 LogFatalI("Exit code %d.", waitCode(status));
  532.         }
  533.         CleanCppOutput(outfd, outfname);
  534.     } else {    /* child... dup and exec cpp */
  535.         if (verbose)
  536.             showargs(cpp_argv);
  537.         dup2(pipefd[0], 0);
  538.         dup2(fileno(outfd), 1);
  539.         close(pipefd[1]);
  540.         execv(cpp, cpp_argv);
  541.         LogFatal("Cannot exec %s.", cpp);
  542.     }
  543. }
  544.  
  545. makeit()
  546. {
  547.     int    pid;
  548.     waitType    status;
  549.  
  550.     /*
  551.      * Fork and exec make
  552.      */
  553.     pid = fork();
  554.     if (pid < 0)
  555.         LogFatal("Cannot fork.", "");
  556.     if (pid) {    /* parent... simply wait */
  557.         while (wait(&status) > 0) {
  558.             errno = 0;
  559.             if (WIFSIGNALED(status))
  560.                 LogFatalI("Signal %d.", waitSig(status));
  561.             if (WIFEXITED(status) && waitCode(status))
  562.                 LogFatalI("Exit code %d.", waitCode(status));
  563.         }
  564.     } else {    /* child... dup and exec cpp */
  565.         if (verbose)
  566.             showargs(make_argv);
  567.         if (make)
  568.             execv(make, make_argv);
  569.         else
  570.             execvp("make", make_argv);
  571.         LogFatal("Cannot exec %s.", make);
  572.     }
  573. }
  574.  
  575. char *CleanCppInput(Imakefile)
  576.     char    *Imakefile;
  577. {
  578.     FILE    *outFile = NULL;
  579.     int    infd;
  580.     char    *buf,        /* buffer for file content */
  581.         *pbuf,        /* walking pointer to buf */
  582.         *punwritten,    /* pointer to unwritten portion of buf */
  583.         *cleanedImakefile = Imakefile,    /* return value */
  584.         *ptoken,    /* pointer to # token */
  585.         *pend,        /* pointer to end of # token */
  586.         savec;        /* temporary character holder */
  587.     struct stat    st;
  588.  
  589.     /*
  590.      * grab the entire file.
  591.      */
  592.     if ((infd = open(Imakefile, O_RDONLY)) < 0)
  593.         LogFatal("Cannot open %s for input.", Imakefile);
  594.     fstat(infd, &st);
  595.     buf = Emalloc(st.st_size+1);
  596.     if (read(infd, buf, st.st_size) != st.st_size)
  597.         LogFatal("Cannot read all of %s:", Imakefile);
  598.     close(infd);
  599.     buf[ st.st_size ] = '\0';
  600.  
  601.     punwritten = pbuf = buf;
  602.     while (*pbuf) {
  603.         /* pad make comments for cpp */
  604.         if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
  605.  
  606.         ptoken = pbuf+1;
  607.         while (*ptoken == ' ' || *ptoken == '\t')
  608.             ptoken++;
  609.         pend = ptoken;
  610.         while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
  611.             pend++;
  612.         savec = *pend;
  613.         *pend = '\0';
  614.         if (strcmp(ptoken, "include")
  615.          && strcmp(ptoken, "define")
  616.          && strcmp(ptoken, "undef")
  617.          && strcmp(ptoken, "ifdef")
  618.          && strcmp(ptoken, "ifndef")
  619.          && strcmp(ptoken, "else")
  620.          && strcmp(ptoken, "endif")
  621.          && strcmp(ptoken, "if")) {
  622.             if (outFile == NULL) {
  623.             tmpImakefile = Strdup(tmpImakefile);
  624.             (void) mktemp(tmpImakefile);
  625.             cleanedImakefile = tmpImakefile;
  626.             outFile = fopen(tmpImakefile, "w");
  627.             if (outFile == NULL)
  628.                 LogFatal("Cannot open %s for write.\n",
  629.                 tmpImakefile);
  630.             }
  631.             fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
  632.             fputs("/**/", outFile);
  633.             punwritten = pbuf;
  634.         }
  635.         *pend = savec;
  636.         }
  637.         pbuf++;
  638.     }
  639.     if (outFile) {
  640.         fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
  641.         fclose(outFile); /* also closes the pipe */
  642.     }
  643.  
  644.     return(cleanedImakefile);
  645. }
  646.  
  647. CleanCppOutput(tmpfd, tmpfname)
  648.     FILE    *tmpfd;
  649.     char    *tmpfname;
  650. {
  651.     char    *input;
  652.     int    blankline = 0;
  653.  
  654.     while(input = ReadLine(tmpfd, tmpfname)) {
  655.         if (isempty(input)) {
  656.             if (blankline++)
  657.                 continue;
  658.             KludgeResetRule();
  659.         } else {
  660.             blankline = 0;
  661.             KludgeOutputLine(&input);
  662.             fputs(input, tmpfd);
  663.         }
  664.         putc('\n', tmpfd);
  665.     }
  666.     fflush(tmpfd);
  667. #ifdef NFS_STDOUT_BUG
  668.     /*
  669.      * On some systems, NFS seems to leave a large number of nulls at
  670.      * the end of the file.  Ralph Swick says that this kludge makes the
  671.      * problem go away.
  672.      */
  673.     ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
  674. #endif
  675. }
  676.  
  677. /*
  678.  * Determine of a line has nothing in it.  As a side effect, we trim white
  679.  * space from the end of the line.  Cpp magic cookies are also thrown away.
  680.  */
  681. isempty(line)
  682.     char    *line;
  683. {
  684.     char    *pend;
  685.  
  686.     /*
  687.      * Check for lines of the form
  688.      *    # n "...
  689.      * or
  690.      *    # line n "...
  691.      */
  692.     if (*line == '#') {
  693.         pend = line+1;
  694.         if (*pend == ' ')
  695.             pend++;
  696.         if (strncmp(pend, "line ", 5) == 0)
  697.             pend += 5;
  698.         if (isdigit(*pend)) {
  699.             while (isdigit(*pend))
  700.                 pend++;
  701.             if (*pend++ == ' ' && *pend == '"')
  702.                 return(TRUE);
  703.         }
  704.     }
  705.  
  706.     /*
  707.      * Find the end of the line and then walk back.
  708.      */
  709.     for (pend=line; *pend; pend++) ;
  710.  
  711.     pend--;
  712.     while (pend >= line && (*pend == ' ' || *pend == '\t'))
  713.         pend--;
  714.     *++pend = '\0';
  715.     return (*line == '\0');
  716. }
  717.  
  718. /*ARGSUSED*/
  719. char *ReadLine(tmpfd, tmpfname)
  720.     FILE    *tmpfd;
  721.     char    *tmpfname;
  722. {
  723.     static boolean    initialized = FALSE;
  724.     static char    *buf, *pline, *end;
  725.     char    *p1, *p2;
  726.  
  727.     if (! initialized) {
  728.         int    total_red;
  729.         struct stat    st;
  730.  
  731.         /*
  732.          * Slurp it all up.
  733.          */
  734.         fseek(tmpfd, 0, 0);
  735.         fstat(fileno(tmpfd), &st);
  736.         pline = buf = Emalloc(st.st_size+1);
  737.         total_red = read(fileno(tmpfd), buf, st.st_size);
  738.         if (total_red != st.st_size)
  739.             LogFatal("cannot read %s\n", tmpMakefile);
  740.         end = buf + st.st_size;
  741.         *end = '\0';
  742.         lseek(fileno(tmpfd), 0, 0);
  743. #ifdef SYSV
  744.         freopen(tmpfname, "w+", tmpfd);
  745. #else    /* !SYSV */
  746.         ftruncate(fileno(tmpfd), 0);
  747. #endif    /* !SYSV */
  748.         initialized = TRUE;
  749.         fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
  750.         fprintf (tmpfd, "# %s\n",
  751.         "$XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $");
  752.  
  753. #ifdef FIXUP_CPP_WHITESPACE
  754.         {
  755.         static char *cpp_warning[] = {
  756. "#",
  757. "# The cpp used on this machine replaces all newlines and multiple tabs and",
  758. "# spaces in a macro expansion with a single space.  Imake tries to compensate",
  759. "# for this, but is not always successful.",
  760. "#",
  761. NULL };
  762.         char **cpp;
  763.  
  764.         for (cpp = cpp_warning; *cpp; cpp++) {
  765.             fprintf (tmpfd, "%s\n", *cpp);
  766.         }
  767.         }
  768. #endif /* FIXUP_CPP_WHITESPACE */
  769.     }
  770.  
  771.     for (p1 = pline; p1 < end; p1++) {
  772.         if (*p1 == '@@' && *(p1+1) == '@@') { /* soft EOL */
  773.             *p1++ = '\0';
  774.             p1++; /* skip over second @@ */
  775.             break;
  776.         }
  777.         else if (*p1 == '\n') { /* real EOL */
  778.             *p1++ = '\0';
  779.             break;
  780.         }
  781.     }
  782.  
  783.     /*
  784.      * return NULL at the end of the file.
  785.      */
  786.     p2 = (pline == p1 ? NULL : pline);
  787.     pline = p1;
  788.     return(p2);
  789. }
  790.  
  791. writetmpfile(fd, buf, cnt)
  792.     FILE    *fd;
  793.     int    cnt;
  794.     char    *buf;
  795. {
  796.     errno = 0;
  797.     if (fwrite(buf, cnt, 1, fd) != 1)
  798.         LogFatal("Cannot write to %s.", tmpMakefile);
  799. }
  800.  
  801. char *Emalloc(size)
  802.     int    size;
  803. {
  804.     char    *p;
  805.  
  806.     if ((p = malloc(size)) == NULL)
  807.         LogFatalI("Cannot allocate %d bytes\n", size);
  808.     return(p);
  809. }
  810.  
  811. #ifdef FIXUP_CPP_WHITESPACE
  812. KludgeOutputLine(pline)
  813.     char    **pline;
  814. {
  815.     char    *p = *pline;
  816.  
  817.     switch (*p) {
  818.         case '#':    /*Comment - ignore*/
  819.         break;
  820.         case '\t':    /*Already tabbed - ignore it*/
  821.             break;
  822.         case ' ':    /*May need a tab*/
  823.         default:
  824.         for (; *p; p++) if (p[0] == ':' && 
  825.                     p > *pline && p[-1] != '\\') {
  826.             if (**pline == ' ')
  827.             (*pline)++;
  828.             InRule = TRUE;
  829.             break;
  830.         }
  831.         if (InRule && **pline == ' ')
  832.             **pline = '\t';
  833.         break;
  834.     }
  835. }
  836.  
  837. KludgeResetRule()
  838. {
  839.     InRule = FALSE;
  840. }
  841. #endif /* FIXUP_CPP_WHITESPACE */
  842.  
  843. char *Strdup(cp)
  844.     register char *cp;
  845. {
  846.     register char *new = Emalloc(strlen(cp) + 1);
  847.  
  848.     strcpy(new, cp);
  849.     return new;
  850. }
  851. @
  852.